from datetime import datetime, timedelta
from typing import Optional
from passlib.context import CryptContext
from jose import JWTError, jwt
import logging

from app.storage.json_storage import storage
from app.models.schemas import UserCreate, UserLogin, UserInfo, Token
from app.config import settings

logger = logging.getLogger(__name__)

# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

class JSONAuthService:
    """基于JSON文件的认证服务"""
    
    def __init__(self):
        self.secret_key = settings.SECRET_KEY
        self.algorithm = "HS256"
        self.access_token_expire_minutes = settings.ACCESS_TOKEN_EXPIRE_MINUTES
        self.refresh_token_expire_days = settings.REFRESH_TOKEN_EXPIRE_DAYS
    
    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """验证密码"""
        return pwd_context.verify(plain_password, hashed_password)
    
    def get_password_hash(self, password: str) -> str:
        """生成密码哈希"""
        return pwd_context.hash(password)
    
    def create_access_token(self, data: dict, expires_delta: Optional[timedelta] = None) -> str:
        """创建访问令牌"""
        to_encode = data.copy()
        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes)
        
        to_encode.update({"exp": expire, "type": "access"})
        encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
        return encoded_jwt
    
    def create_refresh_token(self, data: dict) -> str:
        """创建刷新令牌"""
        to_encode = data.copy()
        expire = datetime.utcnow() + timedelta(days=self.refresh_token_expire_days)
        to_encode.update({"exp": expire, "type": "refresh"})
        encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
        return encoded_jwt
    
    def verify_token(self, token: str, token_type: str = "access") -> Optional[dict]:
        """验证令牌"""
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            if payload.get("type") != token_type:
                return None
            return payload
        except JWTError:
            return None
    
    def create_user(self, user_data: UserCreate) -> UserInfo:
        """创建用户"""
        try:
            # 准备用户数据
            user_dict = {
                'username': user_data.username,
                'email': user_data.email,
                'full_name': user_data.full_name,
                'hashed_password': self.get_password_hash(user_data.password),
                'role': 'user',
                'is_active': True
            }
            
            # 创建用户
            new_user = storage.create_user(user_dict)
            
            logger.info(f"User created: {new_user['username']}")
            return self._dict_to_user_info(new_user)
            
        except Exception as e:
            logger.error(f"Failed to create user: {e}")
            raise
    
    def authenticate_user(self, username: str, password: str) -> Optional[dict]:
        """验证用户凭据"""
        try:
            user = storage.get_user_by_username(username)
            if not user:
                return None
            
            if not self.verify_password(password, user['hashed_password']):
                return None
            
            if not user['is_active']:
                return None
            
            return user
            
        except Exception as e:
            logger.error(f"Authentication error: {e}")
            return None
    
    def login(self, login_data: UserLogin) -> Optional[Token]:
        """用户登录"""
        try:
            user = self.authenticate_user(login_data.username, login_data.password)
            if not user:
                return None
            
            # 更新最后登录时间
            storage.update_user(user['id'], {
                'last_login': datetime.utcnow().isoformat()
            })
            
            # 创建令牌
            access_token = self.create_access_token(
                data={"sub": user['id'], "username": user['username']}
            )
            refresh_token = self.create_refresh_token(
                data={"sub": user['id'], "username": user['username']}
            )
            
            # 存储会话信息（模拟Redis）
            storage.set_session(
                f"user_session:{user['id']}", 
                {
                    'user_id': user['id'],
                    'username': user['username'],
                    'login_time': datetime.utcnow().isoformat()
                },
                expire_seconds=self.access_token_expire_minutes * 60
            )
            
            logger.info(f"User logged in: {user['username']}")
            return Token(
                access_token=access_token,
                refresh_token=refresh_token,
                token_type="bearer",
                expires_in=self.access_token_expire_minutes * 60,
                user_info=self._dict_to_user_info(user)
            )
            
        except Exception as e:
            logger.error(f"Login error: {e}")
            return None
    
    def refresh_token(self, refresh_token: str) -> Optional[Token]:
        """刷新访问令牌"""
        try:
            payload = self.verify_token(refresh_token, "refresh")
            if not payload:
                return None
            
            user_id = payload.get("sub")
            username = payload.get("username")
            
            if not user_id or not username:
                return None
            
            # 验证用户仍然存在且活跃
            user = storage.get_user_by_id(user_id)
            if not user or not user['is_active']:
                return None
            
            # 创建新的令牌
            access_token = self.create_access_token(
                data={"sub": user['id'], "username": user['username']}
            )
            new_refresh_token = self.create_refresh_token(
                data={"sub": user['id'], "username": user['username']}
            )
            
            # 更新会话信息
            storage.set_session(
                f"user_session:{user['id']}", 
                {
                    'user_id': user['id'],
                    'username': user['username'],
                    'refresh_time': datetime.utcnow().isoformat()
                },
                expire_seconds=self.access_token_expire_minutes * 60
            )
            
            return Token(
                access_token=access_token,
                refresh_token=new_refresh_token,
                token_type="bearer",
                expires_in=self.access_token_expire_minutes * 60,
                user_info=self._dict_to_user_info(user)
            )
            
        except Exception as e:
            logger.error(f"Token refresh error: {e}")
            return None
    
    def get_user_by_id(self, user_id: str) -> Optional[dict]:
        """根据ID获取用户"""
        try:
            return storage.get_user_by_id(user_id)
        except Exception as e:
            logger.error(f"Failed to get user {user_id}: {e}")
            return None
    
    def get_user_by_username(self, username: str) -> Optional[dict]:
        """根据用户名获取用户"""
        try:
            return storage.get_user_by_username(username)
        except Exception as e:
            logger.error(f"Failed to get user {username}: {e}")
            return None
    
    def logout(self, user_id: str) -> bool:
        """用户登出"""
        try:
            # 删除会话信息
            storage.delete_session(f"user_session:{user_id}")
            logger.info(f"User logged out: {user_id}")
            return True
        except Exception as e:
            logger.error(f"Logout error: {e}")
            return False
    
    def _dict_to_user_info(self, user_dict: dict) -> UserInfo:
        """将字典转换为UserInfo"""
        return UserInfo(
            id=user_dict['id'],
            username=user_dict['username'],
            email=user_dict['email'],
            full_name=user_dict.get('full_name', ''),
            role=user_dict.get('role', 'user'),
            is_active=user_dict.get('is_active', True),
            created_at=datetime.fromisoformat(user_dict['created_at']),
            last_login=datetime.fromisoformat(user_dict['last_login']) if user_dict.get('last_login') else None
        )
